package validator

import (
	"errors"
	"fmt"
	"reflect"
)

type Validator func(str string) bool
type ParamValidator func(str string, params ...string) bool

var TagTypeMap = map[string]Validator{
	"required":     IsNotNull,
	"email":        IsEmail,
	"numeric":      IsNumeric,
	"int":          IsInt,
	"float":        IsFloat,
	"null":         IsNull,
	"alpha":        IsAlpha,
	"alpha_num":    IsAlphaNum,
	"alpha_dash":   IsAlphaDash,
	"alpha_normal": IsAlphaNormal,
	"ipv4":         IsIPv4,
	"ipv6":         IsIPv6,
	"comment":      HasComment,
	"mobile":       IsMobile,
	"password":     IsPasswordFormat,
}

var ParamTypeMap = map[string]ParamValidator{
	"between": ValueBetween,
	"in":      IsIn,
	"length":  LengthBetween,
	"confirm": IsConfirm,
}

// struct结构体验证
func ValidateStruct(s interface{}) (bool, Error) {
	var err Error
	if s == nil {
		return false, Error{
			Name: "invalid",
			Err:  errors.New("该数据结构体为空"),
		}
	}
	res := true

	val := reflect.ValueOf(s)
	if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
		val = val.Elem()
	}

	// 判断val是否是struct类型
	if val.Kind() != reflect.Struct {
		return false, Error{
			Name: "invalid",
			Err:  errors.New("该数据不是struct类型"),
		}
	}

	// 遍历struct结构体
	for i := 0; i < val.NumField(); i++ {
		valueField := val.Field(i)
		typeField := val.Type().Field(i)
		if typeField.PkgPath != "" {
			continue
		}
		structRes := true
		if valueField.Kind() == reflect.Interface {
			valueField = valueField.Elem()
		}

		// 判断结构体中是否含有子结构体
		if (valueField.Kind() == reflect.Struct ||
			(valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) &&
			typeField.Tag.Get(tagName) != "-" {
			// 判断valid标签的值是否是"optional"
			if typeField.Tag.Get(tagName) == OptionType && IsEmptyValue(valueField) {
				continue
			}
			structRes, err = ValidateStruct(valueField.Interface())
			// 判断值是否通过验证
			if !structRes && err.Err != nil {
				return false, err
			}
		}

		// 获取valid标签的值
		validVal := typeField.Tag.Get(tagName)

		// 判断validType 是否是 -,即忽略验证
		if validVal == "-" {
			return true, Error{
				Name: "valid",
				Err:  nil,
			}
		}
		// 判断valid标签中是否有可选参数,且为空值时,则返回true 例如:`valid:"optional;between:18,50"`,
		if HasOptional(validVal) && IsEmptyValue(valueField) {
			return true, Error{
				Name: "valid",
				Err:  nil,
			}
		}

		// 判断structField的标签中是否含有comment
		if !HasComment(validVal) {
			structName := val.Type().Name()
			fieldName := typeField.Name

			return false, Error{
				Name: "invalid",
				Err:  errors.New(structName + ":" + fieldName + ",comment空值或不存在"),
			}
		}

		// 判断structField标签中是否含有confirm
		if HasConfirm(validVal) {
			if IsNull(fmt.Sprint(valueField)) {
				return false, Error{
					Name: "invalid",
					Err:  errors.New("确认密码为空"),
				}
			}
			// 获取confirm type的值
			confirmValue := TypeValue(TagTypes(validVal), ConfirmType)

			for j := 0; j < val.NumField(); j++ {
				// 判断confirm的值是否与字段名相同
				if confirmValue == val.Type().Field(j).Name {
					// 比较两个字段的值
					isValid := IsConfirm(fmt.Sprint(valueField), fmt.Sprint(val.Field(j)))
					if !isValid {
						// 跳出循环
						return isValid, Error{
							Name: "",
							Err:  errors.New("两次输入的密码不一至"),
						}
					}
				}
			}
		}

		// validType检测
		res, err := tagTypeCheck(valueField, validVal)
		// 有错误立即中止循环
		if !res {
			return res, err
		}
	}

	return res, err
}

// 标签验证 `valid:"required;between:1,3"` | `valid:"-"` | `valid:"optional;between:18,50"` | `valid:"required;"`
// 参数说明:v,表示struct中字段的值; validVal,表示valid标签对应的值,如上一行所示
func tagTypeCheck(columnVal reflect.Value, validVal string) (isValid bool, resErr Error) {
	isValid = true
	if !columnVal.IsValid() {
		return false, Error{
			Name: "invalid",
			Err:  nil,
		}
	}

	// 分解tag的值
	validTypes := TagTypes(validVal)

	// 将columnVal转换成string
	fieldValue := fmt.Sprint(columnVal)
	// 获取comment的值
	commentVal := TypeValue(validTypes, CommentType)

	// 遍历tag的type值
	for i := 0; i < len(validTypes); i++ {
		// 例如:`valid:"required;"` 遍历后为required和"",因为多一个“;”号 | `valid:"required;between:1,3"`,遍历后为required 和 between:1,3
		validItem := validTypes[i]

		// 排除空值,
		if validItem == "" {
			continue
		}
		// 获取对应的typeItem值,例如: validItem 为 between:1,2
		typeItem := TagTypeItems(validItem)
		// 获取
		typeItemKey := typeItem[0] // between

		// 过滤掉对应type的验证,因为上面已经处理过了
		if typeItemKey == OptionType || typeItemKey == CommentType || typeItemKey == ConfirmType {
			continue
		}

		// 检测tag的type是否是已定义的
		if !IsDefineType(typeItemKey) {
			fmt.Println("typeItemKey:", typeItemKey)

			return false, Error{
				Name: "invalid",
				Err:  errors.New("未知的验证类型"),
			}
		}

		var params []string
		// 判断tag的type是否有值,例如:`valid:"required;between:1,3"`
		if IsInTagType(typeItemKey) {
			isValid = TagTypeMap[typeItemKey](fieldValue)
			if !isValid {
				resErr.Err = errors.New(ValidErr(typeItemKey))
			}
		}
		if IsInParamsTagType(typeItemKey) {
			params = TagValItem(typeItem[1])
			isValid = ParamTypeMap[typeItemKey](fieldValue, params...)
			if !isValid {
				resErr.Err = errors.New(ValidErr(typeItemKey, params...))
			}
		}

		// 验证未通过时
		if !isValid {
			resErr.Name = commentVal
			return isValid, resErr
		}
	}
	return isValid, Error{
		Name: "valid",
		Err:  nil,
	}
}
